package pl.edu.agh.anomalyids.lerad.rule;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import pl.edu.agh.anomalyids.lerad.aspect.AbstractAspect;
import pl.edu.agh.anomalyids.lerad.aspect.enums.AspectType;
import pl.edu.agh.anomalyids.lerad.aspect.enums.Match;
import pl.edu.agh.anomalyids.lerad.rule.util.AspectNameComparator;
import pl.edu.agh.anomalyids.network.Packet;

public class Rule implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	private Map<AspectType, AbstractAspect<?>> conditionals;
	private AbstractAspect<?> implication;
	private int nValue;
	private long time;
	private double weight = 1.0;

	public Rule() {
		conditionals = new HashMap<AspectType, AbstractAspect<?>>();
	}

	public void addAspect(AbstractAspect<?> aspect) {
		conditionals.put(aspect.getType(), aspect);
	}

	public void removeAspect(AbstractAspect<?> aspect) {
		removeAspect(aspect.getType());
	}

	public void removeAspect(AspectType type) {
		conditionals.remove(type);
	}

	public void setImplication(AbstractAspect<?> aspect) {
		this.implication = aspect;
	}

	public Collection<AbstractAspect<?>> getConditionals() {
		return conditionals.values();
	}
	
	public Map<AspectType, AbstractAspect<?>> getAspectsMap(){
		return conditionals;
	}

	public AbstractAspect<?> getImplication() {
		return implication;
	}
	
	public int getNValue() {
		return nValue;
	}

	public void setNValue(int nValue) {
		this.nValue = nValue;
	}

	public int getRValue() {
		return implication.getValues().size();
	}
	
	public double getBelief() {
		return (double)nValue/(double)getRValue();
	}

	@Override
	public String toString() {
		if (conditionals.isEmpty() || implication == null) {
			return "";
		}
		String result = "if ";
		for (AspectType key : conditionals.keySet()) {
			result += "(" + key.toString() + " = "
					+ conditionals.get(key).getData().toString() + ") AND ";
		}
		if (result.length() > 5) {
			result = result.substring(0, result.length() - 5);
		}
		result += " THEN (" + implication.getType().toString() + " E {";
		
		for (Object object : implication.getValues()) {
			result += object.toString() + ",";
		}
		
		result = result.substring(0, result.length() - 1);
		result += "})";
						
		result += " BELIEF " + getNValue() + "/" + getRValue();
		
		result += " WEIGHT " + getWeight();
		
		return result;
	}

	public Match validate(Packet packet) {
		for (AspectType type : conditionals.keySet()) {
			if (conditionals.get(type).packetMatchesAspect(packet) != Match.SAME) {
				return Match.UNKNOWN;
			}
		}
		return packetMatchesImplication(packet);
	}

	private Match packetMatchesImplication(Packet packet) {
		return implication.packetMatchesAspect(packet);
	}
	
	@Override
	public int hashCode() {
		List<AbstractAspect<?>> list = new ArrayList<AbstractAspect<?>>(conditionals.values());
		Collections.sort(list, new AspectNameComparator());
		
		StringBuilder builder = new StringBuilder();
		for (AbstractAspect<?> aspect : list) {
			builder.append(aspect.getType());
			builder.append(aspect.getData()); 
		}
		
		return builder.toString().hashCode();
	}
	
	public void resetTimer(){
		time = System.currentTimeMillis() / 1000;
	}
	
	public long getTimeElapsed(){
		return System.currentTimeMillis() / 1000 - time;
	}
	
	public double getWeight(){
		return weight;
	}
	
	public void setWeight(double weight){
		this.weight = weight;
	}
}
